using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Text;
using Konst3d.Efficiency.IO;

/* Layout:
 * SIGN             / uint 4b           / offset  0
 * VERSION          / uint 4b           / offset  4
 * created year     / int 4b            / offset  8
 * created month    / byte 1b           / offset 12
 * created day      / byte 1b           / offset 13
 * created hour     / byte 1b           / offset 14
 * created minute   / byte 1b           / offset 15
 * created second   / byte 1b           / offset 16
 * loginID          / uint 4b           / offset 17
 * passID           / int 4b            / offset 21
 * cryptoID         / int 4b            / offset 25
 * reserved         / 400b              / offset 29
 */

namespace Konst3d.Efficiency.Suite.SparsedBinary
{
    internal class StorageFile : BinaryFile
    {
        #region Constants

        private const uint SIGNATURE                = 0x04040404;
        private const uint VERSION                  = 1;
        private const long OFFSET_SIGNATURE         = 0;
        private const long OFFSET_VERSION           = 4;
        private const int  OFFSET_CREATED_YEAR      = 8;
        private const int  OFFSET_LOGIN_ID          = 17;
        private const int  OFFSET_PASSWORD_ID       = 21;
        private const int  OFFSET_CRYPTO_ENGINE_ID  = 25;

        #endregion // Constants

        #region Properties

        public int LoginID
        {
            get
            {
                return _loginID;
            }

            set
            {
                _writer.Write(value, OFFSET_LOGIN_ID);
                _loginID = value;
            }
        }

        public int PasswordID
        {
            get
            {
                return _passwordID;
            }

            set
            {
                _writer.Write(value, OFFSET_PASSWORD_ID);
                _passwordID = value;
            }
        }

        public int CryptographicEngineID
        {
            get
            {
                return _cryptographicEngineID;
            }
            set
            {
                _writer.Write(value, OFFSET_CRYPTO_ENGINE_ID);
                _cryptographicEngineID = value;
            }
        }

        public DateTime Created
        {
            get
            {
                return _created;
            }
            set
            {
                _writer.Write(value, OFFSET_CREATED_YEAR);
                _created = value;
            }
        }

        #endregion // Properties

        #region Create/Open Methods

        public static int GetCryptographicEngineID(string file)
        {
            Contract.Requires(!String.IsNullOrEmpty(file));
            Contract.Requires(File.Exists(file));

            using(FileStream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
            using(BinaryReader reader = new BinaryReader(stream))
            {
                // TOFIX: check sign & version

                return reader.ReadInt32(OFFSET_CRYPTO_ENGINE_ID);
            }
        }

        public static StorageFile Create(string path, bool overwrite, ICryptographicEngine cryptographicEngine, DateTime created, int loginID, int passwordID, int cryptographicEngineID)
        {
            Contract.Requires(!String.IsNullOrEmpty(path));
            Contract.Requires(cryptographicEngine != null);

            if (File.Exists(path) && !overwrite)
            {
                throw new IOException(String.Format("File \"{0}\" is already exists.", path));
            }

            FileStream stream = null;
            BinaryReader reader = null;
            BinaryWriter writer = null;

            try
            {
                stream = File.Open(path, FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
                reader = new BinaryReader(stream, cryptographicEngine.Encoding);
                writer = new BinaryWriter(stream, cryptographicEngine.Encoding);

                writer.Write(SIGNATURE);
                writer.Write(VERSION);
                writer.Write(created);
                writer.Write(loginID);
                writer.Write(passwordID);
                writer.Write(cryptographicEngineID);
                writer.WriteBytes(0, 400);

                writer.Flush();
                writer.Seek(0, SeekOrigin.Begin);

                return new StorageFile(stream, reader, writer, cryptographicEngine);
            }
            catch
            {
                if (stream != null)
                {
                    stream.Dispose();
                }

                if (reader != null)
                {
                    reader.Dispose();
                }

                if (writer != null)
                {
                    writer.Dispose();
                }

                throw;
            }
        }

        public static StorageFile Open(string path, ICryptographicEngine cryptographicEngine)
        {
            Contract.Requires(!String.IsNullOrEmpty(path));
            Contract.Requires(File.Exists(path));
            Contract.Requires(cryptographicEngine != null);

            FileStream stream = null;
            BinaryReader reader = null;
            BinaryWriter writer = null;

            try
            {
                stream = File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
                reader = new BinaryReader(stream, Encoding.UTF8);
                writer = new BinaryWriter(stream, Encoding.UTF8);

                return new StorageFile(stream, reader, writer, cryptographicEngine);
            }
            catch
            {
                if (stream != null)
                {
                    stream.Dispose();
                }

                if (reader != null)
                {
                    reader.Dispose();
                }

                if (writer != null)
                {
                    writer.Dispose();
                }

                throw;
            }
        }

        #endregion // Create/Open Methods

        #region Constructor

        private StorageFile(FileStream stream, BinaryReader reader, BinaryWriter writer, ICryptographicEngine cryptographicEngine):
            base(stream, reader, writer, SIGNATURE, VERSION)
        {
            Contract.Requires(cryptographicEngine != null);

            _cryptographicEngine = cryptographicEngine;

            _created = _reader.ReadDateTime();
            _loginID = _reader.ReadInt32();
            _passwordID = _reader.ReadInt32();
            _cryptographicEngineID = _reader.ReadInt32();
        }

        #endregion // Constructor

        #region Fields

        private ICryptographicEngine _cryptographicEngine;
        private int _loginID;
        private int _passwordID;
        private int _cryptographicEngineID;
        private DateTime _created;

        #endregion // Fields
    }
}
